﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data.Entity.Design;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data;

namespace HWmanagementDB
{
    /// <summary>
    /// Egy általános adattábla műveleteit implementálja
    /// </summary>
    public class GenericRepository<TEntity> where TEntity : class
    {
        internal ObjectContext context;
        internal ObjectSet<TEntity> dbSet;

        /// <summary>
        /// Konstruktor egy csak olvasható adattábla burkolásához
        /// </summary>
        /// <param name="e">ObjectContext típusú objektum</param>  
        public GenericRepository(ObjectContext e)
        {
            context = e;
            dbSet = context.CreateObjectSet<TEntity>();
        }

        /// <summary>
        /// Lekérdezi, hogy az aktuális adattáblában melyik olszop a kulcs
        /// </summary>
        /// <param name="objectSet">Az a halmaz, amelyből a kulcs értéket szeretnénk</param>
        /// <param name="keyValue">A kulcs értéke</param>
        /// <returns>A halmaz kulcs oszlopa</returns>
        private EntityKey GetEntityKey<T>(ObjectSet<T> objectSet, object keyValue) where T : class
        {
            var entitySetName = objectSet.Context.DefaultContainerName + "." + objectSet.EntitySet.Name;
            var keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
            var entityKey = new EntityKey(entitySetName, new[] { new EntityKeyMember(keyPropertyName, keyValue) });
            return entityKey;
        }

        /// <summary>
        /// A paraméterben megadott feltételeknek megfelelő lekérdezést hajtja végre
        /// </summary>
        /// <param name="filter">Szűrést végez a megadott kifejezés alapján</param>
        /// <param name="order">Rendezést végez a megadott kifejezés alapján</param>
        /// <param name="properties">Egyszerű szöveg, vesszővel elválasztva, amely megadja, hogy mely Navigation property-kel kell a lekérdezéshez csatolni</param>
        /// <returns>A szűrési feltételeknek megfelelő halmaz</returns>
        public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
                                                                                        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> order = null,
                                                                                        string properties = "")
        {
            try
            {
                IQueryable<TEntity> query = dbSet;

                if (filter != null)
                {
                    query = query.Where(filter);
                }

                foreach (var property in properties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    query = query.Include(property);
                }

                if (order != null)
                {
                    return order(query).ToList();
                }
                else
                {
                    return query.ToList();
                }
            }
            catch (ObjectNotFoundException)
            {
                return null;
            }
        }

        /// <summary>
        /// A paraméterben megadott kulcsú objektummal tér vissza
        /// </summary>
        /// <param name="id">Kulcs érték</param>
        /// <returns>Kulcs entitás</returns>  
        /// <exception cref="NotSupportedException.NotSupportedException">Kivétel, ha a művelet nem engedélyezett</exception> 
        public virtual TEntity GetByID(object id)
        {
            try
            {
                return context.GetObjectByKey(this.GetEntityKey<TEntity>(dbSet, id)) as TEntity;
            }
            //nothing is returned by the query
            //maybe this could be done nicer, but not here
            catch (ObjectNotFoundException)
            {
                return null;
            }
        }

        /// <summary>
        /// Egy entitást szúr be az adatbázisba
        /// </summary>
        /// <param name="entity">Entitás</param>
        /// <exception cref="NotSupportedException.NotSupportedException">Kivétel, ha a művelet nem engedélyezett</exception> 
        public virtual void Insert(TEntity entity)
        {
            dbSet.AddObject(entity);
        }

        /// <summary>
        /// Egy entitást töröl az adatbázisból kulcs alapján
        /// </summary>
        /// <param name="id">Kulcs érték</param>
        /// <exception cref="NotSupportedException.NotSupportedException">Kivétel, ha a művelet nem engedélyezett</exception> 
        public virtual void Delete(object id)
        {
            Delete(GetByID(id));
        }

        /// <summary>
        /// Egy entitást töröl az adatbázisból
        /// </summary>
        /// <param name="entity">Entitás</param>
        /// <exception cref="NotSupportedException.NotSupportedException">Kivétel, ha a művelet nem engedélyezett</exception> 
        public virtual void Delete(TEntity entity)
        {
            ObjectStateEntry e;
            context.ObjectStateManager.TryGetObjectStateEntry(entity, out e);
            if (e.State == EntityState.Detached)
            {
                dbSet.Attach(entity);
            }
            dbSet.DeleteObject(entity);
        }

        /// <summary>
        /// Frissít egy entitást az adatbázisban
        /// </summary>
        /// <param name="entity">Entitás</param>
        /// <exception cref="NotSupportedException.NotSupportedException">Kivétel, ha a művelet nem engedélyezett</exception> 
        public virtual void Update(TEntity entity)
        {
            dbSet.Attach(entity);
            ObjectStateEntry e;
            context.ObjectStateManager.TryGetObjectStateEntry(entity, out e);
            e.ChangeState(EntityState.Modified);
        }
    }
}